//Magnus Baunsgaard Kristensen s140168
//Jonathan Yannick Thomassen s125070

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;

public class Billetautomat {

  String kodeord; //Kodeordet kan ikke ændres efter automaten oprettes
  int antalBilletterSolgt, antalKlippekortSolgt; //Bruges til log
  double balance, total; //Penge har typen double for at man kan bruge ører, total er mængden af penge i automaten
  String s; // Bruges som lokal variabel i funktioner der skriver til log
  ArrayList<String> dagLog = new ArrayList<>(); //En ArrayList der indeholder timestamp og type for hver købt billet
  final ArrayList<Billet> billetter = new ArrayList<>();
  final ArrayList<Klippekort> klippekort = new ArrayList<>();
  final ArrayList<Billet> kurvBillet = new ArrayList<>(); // De billetter der lige nu er valgt
  final ArrayList<Klippekort> kurvKlippekort = new ArrayList<>();
  DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(Locale.FRENCH);

  public void setKodeord(String kode) {
    kodeord = kode;
  }

  /**
   * Denne metode tilføjer en billettype til ArrayList'en med billetter, og sætter denne billets
   * navn og pris
   *
   * @param pris Prisen for én af disse billetter
   * @param navn Billettens navn, ordet 'billet' bør ikke medtages
   */
  public void nyBillet(double pris, String navn) {
    Billet nyBilletType;
    nyBilletType = new Billet(pris, navn);
    billetter.add(nyBilletType);
  }

  /**
   * Denne metode tilføjer en klippekorttype til ArrayList'en med klippekort, og sætter denne
   * billets navn og pris
   *
   * @param pris Prisen for ét af disse klippekort
   * @param navn Klippekortets navn, bør ikke indeholde ordet 'klippekort'
   */
  public void nytKlippekort(double pris, String navn) {
    Klippekort nytKlippekort;
    nytKlippekort = new Klippekort(pris, navn);
    klippekort.add(nytKlippekort);
  }

  /**
   * Giver en liste med de billettyper der er tilføjet til automaten
   *
   * @return ArrayList bestående af Billet objekter
   */
  public ArrayList<Billet> getBilletTyper() {
    return billetter;
  }

  /**
   * Giver en liste med de klippekorttyper der er tilføjet til automaten
   *
   * @return ArrayList bestående af Klippekort objekter
   */
  public ArrayList<Klippekort> getKlippekortTyper() {
    return klippekort;
  }

  /**
   * Returnerer de penge der står til rådighed for brugeren
   *
   * @return Penge til rådighed for brugeren
   */
  public double getBalance() {
    return balance;
  }

  /**
   * Returnerer mængden af penge der er i maskinen
   *
   * @return Pengebeholdning i maskinen
   */
  public double getTotal() {
    return total;
  }

  /**
   * Returnerer koden til automaten. Kan bruges i forbindelse med tømning af automaten.
   *
   * @return Kodeordet som String objekt
   */
  public String getKodeord() {
    return kodeord;
  }

  /**
   * sælgBillet udskriver den givne billettype, og tilføjer et timestamp til loggen
   *
   * @param billetType Det billetobjekt der ønskes solgt
   */
  public void sælgBillet(Billet billetType) {
    String billetNavn = billetType.getNavn();
    billetType.udskriv();
    antalBilletterSolgt++;
    s = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(Calendar.getInstance().getTime()) + " Billettype: " + billetNavn;
    dagLog.add(s);
  }

  /**
   * sælgKlippekort udskriver den givne klippekorttype, og tilføjer et timestamp til loggen
   *
   * @param klippekortType Det klippekortobjekt der ønskes købt
   */
  public void sælgKlippekort(Klippekort klippekortType) {
    String billetNavn = klippekortType.getNavn();
    klippekortType.udskriv();
    antalKlippekortSolgt++;
    s = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(Calendar.getInstance().getTime()) + " Billettype: " + billetNavn;
    dagLog.add(s);
  }

  /**
   * Ved at tage størrelsen af de to kurve med hhv. klippekort og billetter i, returneres det
   * samlede antal ting i kurven
   *
   * @return Antallet af objekter i begge kurve (billet- og klippekortskurve)
   */
  public int getKurvStørrelse() {
    return kurvBillet.size() + kurvKlippekort.size();
  }

  /**
   * Tæller antallet af billetter med et bestemt navn i kurven og returnerer det
   *
   * @param billetNavn Navnet på den billet der skal tælles
   * @return Antallet af billetter med det givne navn i kurven
   */
  public int getBilletAntalKurv(String billetNavn) {
    int antal = 0;
    for (int i = kurvBillet.size() - 1; i >= 0; i--) {
      if (billetNavn.equals(kurvBillet.get(i).getNavn())) {
        antal++;
      }
    }
    return antal;
  }

  /**
   * Tæller antallet af klippekort med et bestemt navn i kurven og returnerer det
   *
   * @param klipNavn Navnet på det klippekort der skal tælles
   * @return Antallet af klippekort med det givne navn i kurven
   */
  public int getKlippekortAntalKurv(String klipNavn) {
    int antal = 0;
    for (int i = kurvKlippekort.size() - 1; i >= 0; i--) {
      if (klipNavn.equals(kurvKlippekort.get(i).getNavn())) {
        antal++;
      }
    }
    return antal;
  }

  /**
   * Den samlede kurv er en liste strings som angiver hvilke typer billetter og klippekort, og
   * hvilket antal af disse, kurven indeholder, stykprisen for dem og den samlede pris
   *
   * @return En ArrayList med navn, antal, stykpris og pris i alt for hver type billet og klippekort
   * i kurven
   */
  public ArrayList<String> samletKurv() {
    formatter.applyPattern("#,###,##0.00");
    ArrayList<String> kurv = new ArrayList<>();
    for (Billet billet : getBilletTyper()) {
      if (kurvBillet.indexOf(billet) > -1) {
        kurv.add("(Billet) " + billet.getNavn() + " - " + getBilletAntalKurv(billet.getNavn()) + " á "
            + formatter.format(billet.getPris()) + ",- stk: " + formatter.format((getBilletAntalKurv(billet.getNavn()) * billet.getPris())) + ",-");
      }
    }

    for (Klippekort klip : getKlippekortTyper()) {
      if (kurvKlippekort.indexOf(klip) > -1) {
        kurv.add("(Klippekort) " + klip.getNavn() + " - " + getKlippekortAntalKurv(klip.getNavn()) + " á "
            + formatter.format(klip.getPris()) + ",- stk: " + formatter.format((getKlippekortAntalKurv(klip.getNavn()) * klip.getPris())) + ",-");
      }
    }
    return kurv;
  }

  /**
   * Lægger prisen for alle objekter i kurven sammen og returnerer dem som en double
   *
   * @return Samlet pris for alle objekter i kurven
   */
  public double getKurvPris() {
    double pris = 0;
    for (Billet b : kurvBillet) {
      pris += b.getPris();
    }
    for (Klippekort k : kurvKlippekort) {
      pris += k.getPris();
    }
    return pris;
  }

  /**
   * Tilføjer en billet til billetkurven
   *
   * @param billet Billetten der ønskes tilføjet
   */
  public void tilføjBillet(Billet billet) {
    kurvBillet.add(billet);
  }

  /**
   * Tilføjer et klippekort til klippekortkurven
   *
   * @param klippekort Klippekortet der ønskes tilføjet
   */
  public void tilføjKlippekort(Klippekort klippekort) {
    kurvKlippekort.add(klippekort);
  }

  /**
   * Sletter alle nuværende objekter i begge kurve
   */
  public void rydKurv() {
    kurvBillet.clear();
    kurvKlippekort.clear();
  }

  /**
   * Udskriver alle billetter og klippekort i kurven til terminalen. Lige nu svarer det til at de
   * bliver sendt til en printer og fysisk printet, da den er adskilt fra runtime GUI'en
   */
  private void udskrivKurv() {
    for (Billet billet : kurvBillet) {
      sælgBillet(billet);
    }
    for (Klippekort klip : kurvKlippekort) {
      sælgKlippekort(klip);
    }
    rydKurv();
    System.out.println();
    System.out.println("-------------------------------------------");
  }

  /**
   * købKurv tager en boolean som fortæller den om den er i testtilstand. Hvis dener det, kræver den
   * ingen penge. Ellers tjekker den om der er pange nok ytil at købe alle billetter i kurven, og
   * hvis der er det, printes billetterne. Den returnerer en boolean som bruges til at afgøre om
   * billetterne er blevet printet
   *
   * @param test En boolean der fortæller metoden om den er i testtilstand, i hvilket tilfælde der
   * ikke trækkes penge
   * @return Returnerer en boolean som er true hvis køet gennemførtes og false hvis der ikke var
   * penge nok
   */
  public boolean købKurv(boolean test) {
    if (test) {
      udskrivKurv();
      return true;
    } else {
      if (balance >= getKurvPris()) {
        balance -= getKurvPris();
        udskrivKurv();
        pengeRetur();
        return true;
      } else {
        return false;
      }
    }
  }

  /**
   * Når der indsættes penge øges både balance, for at angive hvor mange penge kunden har til
   * rådighed, og total, for at angive mængden af penge i maskinen
   *
   * @param beløb Et beløb i kroner og ører som indsættes i automaten
   */
  public void indsætPenge(double beløb) {
    balance += beløb;
    total += beløb;
  }

  /**
   * pengeRetur skriver til terminalen hvor mange penge der gives retur, hvilket repræsenterer at
   * der gives penge tilbage på samme måde som printning af billetter bliver håndteret. balancen
   * sættes så til nul, og de penge der forsvinder fra maskinen trækkes fra total
   */
  public void pengeRetur() {
    System.out.println();
    System.out.println(balance + " kr gives retur");
    total -= balance;
    balance = 0;
    System.out.println();
    System.out.println("-------------------------------------------");
  }

  /**
   * udskrivLog printer flere ting i terminalen (hvilket igen betyder at det printes i den fysiske
   * maskine). Først printes alle de timestamps som i løbet af dagen er blev skrevet til loggen. Så
   * udskrives en optælning hvor mange billetter der er blevet solgt af hver type.
   */
  public void udskrivLog() {
    System.out.println();
    for (String entry : dagLog) {
      System.out.println(entry);
    }
    System.out.println("Der er blevet solgt " + antalBilletterSolgt + " billetter og " + antalKlippekortSolgt + " klippekort");
    for (Billet b : billetter) {
      System.out.println("Der er blevet solgt " + b.getAntalSolgt() + " billetter med navn: " + b.getNavn());
    }
    for (Klippekort k : klippekort) {
      System.out.println("Der er blevet solgt " + k.getAntalSolgt() + " klippekort af type: " + k.getNavn());
    }
    System.out.println();
    System.out.println("-------------------------------------------");
  }

  /**
   * reset bruges til at tømme loggen og sætte alle de værdier der bruges under runtime til nul
   */
  public void reset() {
    balance = 0;
    total = 0;
    antalBilletterSolgt = 0;
    antalKlippekortSolgt = 0;
    dagLog = new ArrayList<>();
    rydKurv();
  }

  /**
   * tømMaskine udskriver først loggen og printer så hvor mange penge der burde være i terminalen.
   * Til sidst kalder den så reset for at gøre automaten klar til endnu en dag
   */
  public void tømMaskine() {
    udskrivLog();
    System.out.println("Der er " + total + " kr. i automaten");
    reset();
  }

  public void skrivFil(String filnavn) throws IOException {
    FileWriter fil = new FileWriter(filnavn + ".txt");
    try (PrintWriter ud = new PrintWriter(fil)) {
      ud.println(kodeord);

      ud.println(billetter.size());
      ud.println(klippekort.size());

      for (Billet billet : billetter) {
        ud.println(billet.getPris());
        ud.println(billet.getNavn());
      }

      for (Klippekort klip : klippekort) {
        ud.println(klip.getPris());
        ud.println(klip.getNavn());
      }
    }
  }
}
